package lab.craft;

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.util.HashMap;

/**
 * 简单脚本解释器。
 * 仅支持 simpleParser.java 中的语法
 *
 *
 */
public class SimpleScript {
    private final HashMap<String, Integer> variables = new HashMap<>();
    // 是否启用详细输出
    private static boolean verbose = false;

    public static void main(String[] args) {
        if (args.length > 0 && args[0].equals("-v")) {
            verbose = true;
            System.out.println("verbose mde");
        }

        System.out.println("simple script language");
        SimpleParser parser = new SimpleParser();
        SimpleScript script = new SimpleScript();
        BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));

        // 字符串命令
        StringBuilder scriptText = new StringBuilder();
        String prompt = ">>>";
        System.out.print("\n"+ prompt);

        while (true) {
            try {
                String line = reader.readLine().trim();
                if (line.equals("exit();")) {
                    System.out.println("bye bye");
                    break;
                }
                scriptText.append(line).append("\n");
                if (line.endsWith(";")) {
                    ASTNode tree = parser.parse(scriptText.toString());
                    if (verbose) {
                        parser.dumpAST(tree, "");
                    }
                    script.evaluate(tree, "");
                    System.out.print("\n"+ prompt);
                    scriptText = new StringBuilder();
                }
            } catch (Exception e) {
                System.out.println(e.getLocalizedMessage());
                System.out.print("\n"+ prompt);
                scriptText = new StringBuilder();
            }
        }

    }

    /**
     * 遍历AST，计算值
     * @param node 节点
     * @param indent 填充符
     * @return result
     * @throws Exception error exception
     */
    private Integer evaluate(ASTNode node, String indent) throws Exception {
        Integer result = null;
        if (verbose) {
            System.out.println(indent + "calculating: " + node.getType());
        }

        switch (node.getType()) {
            case Program:
                for (ASTNode child : node.getChildren()) {
                    result = evaluate(child, indent);
                }
                break;
            case Additive:
                // 加法
                ASTNode child1 = node.getChildren().get(0);
                Integer value1 = evaluate(child1, indent + "\t");
                ASTNode child2 = node.getChildren().get(1);
                Integer value2 = evaluate(child2, indent + "\t");
                if (node.getText().equals("+")) {
                    result = value1 + value2;
                } else {
                    result = value1 - value2;
                }
                break;
            case Multiplicative:
                // 乘除法
                child1 = node.getChildren().get(0);
                value1 = evaluate(child1, indent + "\t");
                child2 = node.getChildren().get(1);
                value2 = evaluate(child2, indent + "\t");
                if (node.getText().equals("*")) {
                    result = value1 * value2;
                } else {
                    if (value2 == 0) {
                        throw new Exception("divided by zero");
                    }
                    result = value1 / value2;
                }
                break;
            case IntLiteral:
                // 赋值
                result = Integer.parseInt(node.getText());
                break;
            case Identifier:
                String varName = node.getText();
                if (variables.containsKey(varName)) {
                    Integer value = variables.get(varName);
                    if (value != null) {
                        result = value;
                    } else {
                        throw new Exception("variable " + varName + " has not set value yet");
                    }
                } else {
                    throw new Exception("unknown variable: " + varName);
                }
                break;
            case AssignmentStmt:
                varName = node.getText();
                if (!variables.containsKey(varName)) {
                    throw new Exception("unknown variable: " + varName);
                }
                //接着执行下面的代码
            case IntDeclaration:
                varName = node.getText();
                Integer varValue = null;
                if (node.getChildren().size() == 1) {
                    ASTNode child = node.getChildren().get(0);
                    result = evaluate(child, indent + "\t");
                    varValue = result;
                }
                variables.put(varName, varValue);
                break;
            default:
                System.out.println("unsupported node type " + node.getType() + " to evaluate");
        }
        if (verbose) {
            System.out.println(indent + "Result: " + result);
        } else if (indent.equals("")) {
            // 顶层语句
            if (node.getType() == ASTNodeType.IntDeclaration || node.getType() == ASTNodeType.AssignmentStmt) {
                // 输出赋值后的结果
                System.out.println(node.getText() + ": " + result);
            } else if (node.getType() != ASTNodeType.Program) {
                System.out.println(result);
            }
        }
        return result;
    }
}
